package cs348a.test.pilot;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import cs348a.data.CurveEvaluator;
import cs348a.data.SequenceData;
import cs348a.opengl.JoglPerspective;

/**
 * <code>Pilot</code> pilots the view.
 * 
 * @author H.S Kim
 */
public class SegmentPilot extends AbstractPilot {

	private JoglPerspective perspective;
	
	private SequenceData seqData;
	private CurveEvaluator ce = new CurveEvaluator();
	
	/** The number of segments per curve */
	private int numSeg = 10;

	/** The current segment */
	private int currCur = 0;
	/** The current segment */
	private int currSeg = 0;
	
	private PilotThread pt;
	
	/**
	 * Constructor
	 * 
	 * @param p
	 */
	public SegmentPilot(JoglPerspective p){
		perspective = p;
	}
	
	@Override
	public void setSequenceData(SequenceData sd) {
		seqData = sd;
		
		seqData.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) {
				// TODO: verify values
				currCur++;
				currSeg = 0;
				
				// update evaluator
				ce.setControlPoints(
						seqData.siteList.get(2*currCur), 
						seqData.siteList.get(2*currCur+1), 
						seqData.siteList.get(2*currCur+2));
				
				currentPoint = ce.evaluate(currSeg/(float)numSeg); 
				goToPosition(currentPoint);
				fireChanged(currentPoint);
			}
		});
		
		// initialize curve evaluator
		ce.setControlPoints(
				seqData.siteList.get(2*currCur), 
				seqData.siteList.get(2*currCur+1), 
				seqData.siteList.get(2*currCur+2));
	}
	
	@Override
	public int getCurveNo() {
		return currCur;
	}
	
	@Override
	public int getSegmentNo(){
		return currSeg;
	}
	
	@Override
	public void setPosition(int curveNo, int segNo) {
		// TODO: validate input
		currCur = curveNo;
		currSeg = segNo;
		
		// update evaluator
		ce.setControlPoints(
				seqData.siteList.get(2*currCur), 
				seqData.siteList.get(2*currCur+1), 
				seqData.siteList.get(2*currCur+2));
		
		currentPoint = ce.evaluate(currSeg/(float)numSeg); 
		goToPosition(currentPoint);
		fireChanged(currentPoint);
	}

	@Override
	public void startTour() {
		if(seqData == null) return;
		// start thread
		if(pt == null || !pt.isRunning()) {
			pt = new PilotThread();
			pt.start();
		}
	}
	
	@Override
	public void stopTour() {
		if(pt == null) return;
		pt.abort();
	}
	
	/**
	 * Resets the tour
	 */
	public void resetTour() {
		currCur = 0;
		currSeg = 0;
		
		ce.setControlPoints(
				seqData.siteList.get(2*currCur), 
				seqData.siteList.get(2*currCur+1), 
				seqData.siteList.get(2*currCur+2));
		
		pt = null;
	}
	
	@Override
	public boolean isRunning() {
		return pt != null;
	}
	
	/**
	 * Set to position
	 * 
	 * @param p
	 */
	public void goToPosition(float[] p) {
		// set perspective
		p[2] += zOffset;
		perspective.viewPosition = p;
		
		float[] p1 = p.clone(); 
		p1[2] = 0;
		perspective.objectPosition = p1;

		perspective.upDirection = new float[] {0, 1, 0};
	}
	
	public class PilotThread extends Thread {
		private boolean runFlag = true;
		
		public void run(){
			
			while(runFlag){
				try {
					currentPoint = ce.evaluate(currSeg/(float)numSeg); 
					goToPosition(currentPoint);
					fireChanged(currentPoint);
					
					// increment counters
					currSeg++;
					
					if(currSeg == numSeg) {
						currCur++;
						currSeg = 0;
												
						if(currCur == seqData.siteList.size()/2) {
							// go to last position 
							sleep(timeInterval);
							currentPoint = ce.evaluate(1);
							goToPosition(currentPoint);
							fireChanged(currentPoint);
							
							resetTour();
							return;
						} else {
							// reset sites
							ce.setControlPoints(
									seqData.siteList.get(2*currCur), 
									seqData.siteList.get(2*currCur+1), 
									seqData.siteList.get(2*currCur+2));
						}
					}
					
					sleep(timeInterval);

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void abort(){
			runFlag = false;
		}
		
		public boolean isRunning() {
			return runFlag;
		}
	}
}
